package ift585.serveur;


import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.DatagramPacket;

public class FenetreServeur {
	// Taille des éléments
	private static final int TAILLE_PAQUET = 1024;
	private static final int TAILLE_TABLEAU = 20;
	private static final int TAILLE_FENETRE = 4;
	
	// Élément représentant la fin de fichier
	public static final int FIN_FICHIER = TAILLE_TABLEAU + 1;
	
	// indicateur de position du début de la fenêtre
	private int debutFenetre = 0;
	
	// tableau de stockage
	private ReceiveState[] tableEtat = null;
	private byte[] tablePaquet[] = null;
	
	// Classe qui gère la remplissage des fenêtres
	private FileCombinerUtility combiner;
	
	public FenetreServeur(String fileName) throws FileNotFoundException{
		debutFenetre = 0;
		tableEtat = new ReceiveState[TAILLE_TABLEAU];
		
		for(int i = 0; i < TAILLE_TABLEAU ; i++){
			tableEtat[i] = ReceiveState.notReceived;
		}
		
		combiner = new FileCombinerUtility(fileName);
		
		tablePaquet = new byte[TAILLE_TABLEAU][TAILLE_PAQUET];
	}
	
	public byte[] getNextAck(){
		return combiner.getNextAck(debutFenetre);
	}
	
	public byte[] getNextAck(byte[] dataReceived) throws IOException{ // Apr�s r�ception d'un ack
		// Trouve le numéro du message reçu
		int ackSeq = combiner.getAckNumber(dataReceived);
		
		// Vérifier si l'on a reçu le dernier fichier et que l'on est donc prêt à terminer
		if (ackSeq == FIN_FICHIER){
			return combiner.getNextAck(FIN_FICHIER);
		}
		
		// Vérifier que le fichier n'est pas hors de la fenetre ou déjà recu
		if(ackIsValid(ackSeq)){

			tableEtat[ackSeq] = ReceiveState.ack;
			tablePaquet[ackSeq] = dataReceived;	
			
			while(tableEtat[debutFenetre] == ReceiveState.ack){
				
				combiner.makeFile(tablePaquet[debutFenetre]);
				tableEtat[debutFenetre] = ReceiveState.notReceived;
				debutFenetre = (debutFenetre + 1) % TAILLE_TABLEAU;	
			}

		}
		// renvoie le dernier Ack
		byte[] nextAck = new byte[1];
		nextAck[0] = (byte)debutFenetre;
		return nextAck;
	}
	
	/*
	 * Valide si le ack reçu se trouve dans la fenetre et s'il n'Est pas déjà reconnu
	 */
	private boolean ackIsValid(int ackSeq){
		// Détermine la façon de voir s'il est dans la fentre car buffer circulaire
		if(debutFenetre <= (TAILLE_TABLEAU - TAILLE_FENETRE)){
			// Valide si le ack est dans la fenetre
			if(debutFenetre <= ackSeq && ackSeq <= (TAILLE_TABLEAU -1)){
				// valide si déjà reconnue
				return(tableEtat[ackSeq] != ReceiveState.ack);
			}
		}
		else{
			// Valide si le ack est dans la fenetre
			if(!(((debutFenetre + TAILLE_FENETRE)%TAILLE_TABLEAU) < ackSeq && ackSeq < debutFenetre)){
				return(tableEtat[ackSeq] != ReceiveState.ack);
			}
		}
		return false;
	}
}
